﻿using System;
using System.Collections.Generic;
using System.Controls.Extensions;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.Windows.Media;

namespace System.Controls.Converters
{
  public class BrushTransform : IValueConverter
  {
    #region Properties

    public BrushMode Mode { get; set; }
    public int Offset { get; set; }
    public bool IsHorizontal { get; set; }
    public bool IsReversed { get; set; }

    #endregion

    public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
    {
      var brush = value as Brush;

      if ( brush == null )
        return null;

      switch ( Mode )
      {
      case BrushMode.Glass:
        return GlassBrush( brush );

      case BrushMode.Saturation:
        return Saturation( brush );

      case BrushMode.Reverse:
        return Reverse( brush );

      case BrushMode.GrayScale:
        return GrayScale( brush );

      case BrushMode.Hue:
        return Hue( brush );

      case BrushMode.Luminance:
        return Luminance( brush );
      }

      return brush;
    }

    public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
    {
      return null;
    }

    private object Luminance( Brush brush )
    {
      var colorBrush = brush as SolidColorBrush;
      if ( colorBrush != null )
      {
        var col = colorBrush.Color;

        return new SolidColorBrush( col.AdjustHsl( 0, 0, Offset ) );
      }

      if ( !( brush is LinearGradientBrush ) )
        return brush;

      var lib = (LinearGradientBrush)brush;
      var gsc = new GradientStopCollection();

      foreach ( var item in lib.GradientStops )
        gsc.Add( new GradientStop( item.Color.AdjustHsl( 0, 0, Offset ), item.Offset ) );

      return new LinearGradientBrush( gsc, lib.StartPoint, lib.EndPoint )
             {
               ColorInterpolationMode = lib.ColorInterpolationMode,
               MappingMode = lib.MappingMode,
               SpreadMethod = lib.SpreadMethod
             };
    }

    private object Hue( Brush brush )
    {
      var colorBrush = brush as SolidColorBrush;
      if ( colorBrush != null )
      {
        var col = colorBrush.Color;

        return new SolidColorBrush( col.AdjustHsl( Offset, 0, 0 ) );
      }

      if ( !( brush is LinearGradientBrush ) )
        return brush;

      var lib = (LinearGradientBrush)brush;
      var gsc = new GradientStopCollection();

      foreach ( var item in lib.GradientStops )
        gsc.Add( new GradientStop( item.Color.AdjustHsl( Offset, 0, 0 ), item.Offset ) );

      return new LinearGradientBrush( gsc, lib.StartPoint, lib.EndPoint )
             {
               ColorInterpolationMode = lib.ColorInterpolationMode,
               MappingMode = lib.MappingMode,
               SpreadMethod = lib.SpreadMethod
             };
    }

    private static object GrayScale( Brush brush )
    {
      var solidColorBrush = brush as SolidColorBrush;
      if ( solidColorBrush != null )
      {
        var col = solidColorBrush.Color;
        var avg = (byte)( ( col.R + col.G + col.B )/3 );

        return new SolidColorBrush( Color.FromArgb( col.A, avg, avg, avg ) );
      }

      if ( !( brush is LinearGradientBrush ) )
        return brush;

      var lib = (LinearGradientBrush)brush;
      var gsc = new GradientStopCollection();

      foreach ( var item in lib.GradientStops )
      {
        var avg = (byte)( ( item.Color.R + item.Color.G + item.Color.B )/3 );

        gsc.Add( new GradientStop( Color.FromArgb( item.Color.A, avg, avg, avg ), item.Offset ) );
      }

      return new LinearGradientBrush( gsc, lib.StartPoint, lib.EndPoint )
             {
               ColorInterpolationMode = lib.ColorInterpolationMode,
               MappingMode = lib.MappingMode,
               SpreadMethod = lib.SpreadMethod
             };
    }

    private static object Reverse( Brush brush )
    {
      if ( !( brush is LinearGradientBrush ) )
        return brush;

      var lib = (LinearGradientBrush)brush;
      var gsc = new GradientStopCollection();

      foreach ( var item in lib.GradientStops )
        gsc.Add( new GradientStop( item.Color, 1 - item.Offset ) );

      return new LinearGradientBrush( gsc, lib.StartPoint, lib.EndPoint )
             {
               ColorInterpolationMode = lib.ColorInterpolationMode,
               MappingMode = lib.MappingMode,
               SpreadMethod = lib.SpreadMethod
             };
    }

    private object Saturation( Brush brush )
    {
      var solidColorBrush = brush as SolidColorBrush;
      if ( solidColorBrush != null )
      {
        var col = solidColorBrush.Color;

        return new SolidColorBrush( col.AdjustHsl( 0, Offset, 0 ) );
      }

      if ( !( brush is LinearGradientBrush ) )
        return brush;

      var lib = (LinearGradientBrush)brush;
      var gsc = new GradientStopCollection();

      foreach ( var item in lib.GradientStops )
        gsc.Add( new GradientStop( item.Color.AdjustHsl( 0, Offset, 0 ), item.Offset ) );

      return new LinearGradientBrush( gsc, lib.StartPoint, lib.EndPoint )
             {
               ColorInterpolationMode = lib.ColorInterpolationMode,
               MappingMode = lib.MappingMode,
               SpreadMethod = lib.SpreadMethod
             };
    }

    private object GlassBrush( Brush brush )
    {
      if ( !( brush is SolidColorBrush ) )
        return brush;

      var col = ( (SolidColorBrush)brush ).Color;
      var gsc = new GradientStopCollection();

      if ( IsReversed )
      {
        gsc.Add( new GradientStop( col.AdjustHsl( 0, 30, Offset ), 0 ) );
        gsc.Add( new GradientStop( col.AdjustHsl( 30, 0, 0 ), 0.7 ) );
        gsc.Add( new GradientStop( col.AdjustHsl( 0, -50, 50 ), 1 ) );
      }
      else
      {
        gsc.Add( new GradientStop( col.AdjustHsl( 0, -50, 50 ), 0 ) );
        gsc.Add( new GradientStop( col.AdjustHsl( 30, 0, 0 ), 0.3 ) );
        gsc.Add( new GradientStop( col.AdjustHsl( 0, 30, Offset ), 1 ) );
      }

      return new LinearGradientBrush( gsc, IsHorizontal ? 90 : 0 );
    }
  }
}
